//
// SAGEPointer capture code for Mac
// Author: Ratko Jagodic, Modified by Hyejung Hur
// 
// Complile using the following command line:
// gcc -Wall -o macCapture macCapture.c -framework ApplicationServices 
//

#import <AppKit/AppKit.h>
#include <sys/time.h>

#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif

// globals
bool captured = false;
int screenWidth = 1024;
int screenHeight = 768;
bool ctrl = false;
int shiftCount = 0;
struct timeval tv_start;
double firstClickTime, lastMsgTime, firstEdgeTime, capturedTime;

int flagDiff;
CGEventFlags new, old, temp;
CFMachPortRef      eventTap;
CFRunLoopSourceRef runLoopSource;

// touch related
CGPoint initialLocation;
NSTouch *_initialTouches[3];
NSTouch *_currentTouches[3];
int _initialTouchCount = 0;
int _currentTouchCount = 0;
int _maxTouchCount = 0;
bool _begin_flag = false;
double _prev_time = 0.0; 
int _secondDir = -1;
float _magnification = 0;
int _rotation = 0;

// messages to SAGE (dim)
#define MOVE 1
#define CLICK 2
#define WHEEL 3
#define COLOR 4
#define DOUBLE_CLICK 5
#define TWO_FINGERS 6 
#define THREE_FINGERS 7 
#define MAGNIFY 8 
#define ROTATE 9 
#define HAS_DOUBLE_CLICK 10

// msgs to the python program
#define CAPTURED 11
#define RELEASED 12
#define QUIT 13
#define CHANGE_EDGE 14
#define DISABLE_CORNERS 15
#define CONNECT_TWO 16
#define CONNECT_THREE 17

// button IDs
#define LEFT 1
#define RIGHT 2
#define MIDDLE 3
#define UP 4 
#define DOWN 5 

// how long does a cursor have to hove at the edge of a screen before its captured/released?
#define CURSOR_HOVER_TIME 0.0  

// screen edges to use for pointer capture
#define LEFT_EDGE   1
#define RIGHT_EDGE  2
#define TOP_EDGE    3
#define BOTTOM_EDGE 4
int captureEdge = RIGHT_EDGE;
bool disableCorners = false;

// how quickly to double tap in order to capture/release the cursor
#define DOUBLE_CLICK_SPEED  0.3

#define HIDE_CURSOR_DELAY 0.1  //seconds



//-----------------------------------------------------------------------
//          TIME MEASUREMENT
//-----------------------------------------------------------------------

double getTime()  // in seconds... double 
{
    double timeStamp;
    struct timeval tv;

    gettimeofday(&tv, NULL);
    timeStamp = (double)(tv.tv_sec-tv_start.tv_sec)+(double)(tv.tv_usec-tv_start.tv_usec)/1000000;

    return timeStamp;
}



//-----------------------------------------------------------------------
//           Carbon mouse and keyboard event capturing
//-----------------------------------------------------------------------



void sendMsgi(int msgId, int p1){
    printf("%d %d\n", msgId, p1);
    fflush(stdout);
}

void sendMsgii(int msgId, int p1, int p2){
    printf("%d %d %d\n", msgId, p1, p2);
    fflush(stdout);
}

void sendMsgff(int msgId, float p1, float p2){
    printf("%d %f %f\n", msgId, p1, p2);
    fflush(stdout);
}

void toggleCapture()
{
    if (captured) { 
        captured = false;
        fprintf(stderr, "\n\nRELEASED!!!!\n");
        printf("%d\n", RELEASED);
        fflush(stdout);
        CGDisplayShowCursor(kCGNullDirectDisplay);
    } 
    else {
        fprintf(stderr, "\n\n===> CAPTURED\n");
        printf("%d\n", CAPTURED);
        printf("%d\n", HAS_DOUBLE_CLICK);
        fflush(stdout);
        captured = true;
        capturedTime = getTime();
        //CGDisplayHideCursor(kCGNullDirectDisplay);
    }
}

// hyejung
NSPoint calcDeltaOrigin() 
{
    if (!(_initialTouches[0] && _initialTouches[1] && _currentTouches[0] && _currentTouches[1])) return NSZeroPoint;
    
    CGFloat x1 = MIN(_initialTouches[0].normalizedPosition.x, _initialTouches[1].normalizedPosition.x);
    CGFloat x2 = MIN(_currentTouches[0].normalizedPosition.x, _currentTouches[1].normalizedPosition.x);
    CGFloat y1 = MIN(_initialTouches[0].normalizedPosition.y, _initialTouches[1].normalizedPosition.y);
    CGFloat y2 = MIN(_currentTouches[0].normalizedPosition.y, _currentTouches[1].normalizedPosition.y);

    NSSize deviceSize = _initialTouches[0].deviceSize;
    NSPoint delta;
    delta.x = (x2 - x1) * deviceSize.width;
    delta.y = (y2 - y1) * deviceSize.height;
    return delta;
}

// main callback for mouse and keyboard events
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eventRef, void *refcon)
{
    CGEventRef retVal = eventRef;
    bool doubleClick = (CGEventGetIntegerValueField(eventRef, kCGMouseEventClickState) == 2);	
    double now = getTime();

    NSEvent *event = [NSEvent eventWithCGEvent:eventRef];
    // filter out events which do not match the mask

    // The incoming mouse position.
    CGPoint location = CGEventGetLocation(eventRef);
    // hyejung
    if(event.type == NSEventTypeGesture) {
        if (captured) {
		NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseTouching inView:nil];
		if (touches.count >= 2) {
			NSArray *array = [touches allObjects];
			NSTouch *touch;
       			touch = [[array objectAtIndex:0] retain];

			if (touch.phase == NSTouchPhaseBegan) {
       				_initialTouches[0] = [[array objectAtIndex:0] retain];
       				_initialTouches[1] = [[array objectAtIndex:1] retain];
       				_currentTouches[0] = [[array objectAtIndex:0] retain];
       				_currentTouches[1] = [[array objectAtIndex:1] retain];
				if (touches.count > 2) {
       					_initialTouches[2] = [[array objectAtIndex:2] retain];
       					_currentTouches[2] = [[array objectAtIndex:2] retain];
				}
				_initialTouchCount = touches.count;
				_maxTouchCount = _initialTouchCount;
				_begin_flag = true;
				_secondDir = -1;
				_magnification = 0;
			}
			else if (touch.phase == NSTouchPhaseMoved) {
				if (_begin_flag == true) { 
					_currentTouchCount = touches.count;
					if (_maxTouchCount < _currentTouchCount) 
						_maxTouchCount = _currentTouchCount;

        				//[_currentTouches[0] release];
        				//[_currentTouches[1] release];
					//if (_initialTouchCount > 2) 
        				//	[_currentTouches[2] release];

					NSTouch *touch = [array objectAtIndex:0];
        				if ([touch.identity isEqual:_initialTouches[0].identity]) {
						_currentTouches[0] = [touch retain];
        				} else if ([touch.identity isEqual:_initialTouches[1].identity]) {
						_currentTouches[1] = [touch retain];
					} else if (_initialTouchCount > 2) {
        					if ([touch.identity isEqual:_initialTouches[2].identity]) {
							_currentTouches[2] = [touch retain];
						}
					} 

					touch = [array objectAtIndex:1];
        				if ([touch.identity isEqual:_initialTouches[0].identity]) {
						_currentTouches[0] = [touch retain];
        				} else if ([touch.identity isEqual:_initialTouches[1].identity]) {
						_currentTouches[1] = [touch retain];
					} else if (_initialTouchCount > 2) {
        					if ([touch.identity isEqual:_initialTouches[2].identity]) {
							_currentTouches[2] = [touch retain];
						}
					} 

					if (touches.count > 2) { 
						touch = [array objectAtIndex:2];
        					if ([touch.identity isEqual:_initialTouches[0].identity]) {
							_currentTouches[0] = [touch retain];
        					} else if ([touch.identity isEqual:_initialTouches[1].identity]) {
							_currentTouches[1] = [touch retain];
						} else if (_initialTouchCount > 2) {
        						if ([touch.identity isEqual:_initialTouches[2].identity]) {
								_currentTouches[2] = [touch retain];
							}
						} 
					}

					/*
					if (_secondDir == -1) {
						double diff = now -_prev_time;
						if (diff >= 0.2)  {
							_secondDir = -1;
							NSPoint deltaOrigin = calcDeltaOrigin();
							_secondDir = LEFT; 
							float deltaX = fabs(deltaOrigin.x); 
							float deltaY = fabs(deltaOrigin.y); 
							if (deltaX > deltaY) {
								// left and right
								if (deltaOrigin.x > 0)
									_secondDir = RIGHT;
							} else {
								// up and down 
								if (deltaOrigin.y > 0)
									_secondDir = UP;
								else 
									_secondDir = DOWN;
							}
							_initialTouches[0] = _currentTouches[0];
							_initialTouches[1] = _currentTouches[1];
							fprintf(stderr, "first = %d\n", _secondDir);
						}
						_prev_time = now;
					}
					*/
				}
			}
			else if (touch.phase == NSTouchPhaseEnded) {
				fprintf(stderr, "touch -------- ended... \n");
			}
		}
            	retVal = NULL;
        }
    }
    else if(event.type == NSEventTypeBeginGesture) {
        if (captured) {
		if (_begin_flag == false) {
			NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseTouching inView:nil];
			if (touches.count >= 2) {
				NSArray *array = [touches allObjects];
       				_initialTouches[0] = [[array objectAtIndex:0] retain];
       				_initialTouches[1] = [[array objectAtIndex:1] retain];
       				_currentTouches[0] = [[array objectAtIndex:0] retain];
       				_currentTouches[1] = [[array objectAtIndex:1] retain];
				_begin_flag = true;
				_initialTouchCount = touches.count;
				_maxTouchCount = _initialTouchCount;
			}
		}
            	retVal = NULL;
	}
    }
    else if(event.type == NSEventTypeEndGesture) {
        if (captured) {
		if (_magnification != 0)  {
                    sendMsgff(MAGNIFY, _magnification, 0);
        	    fprintf(stderr, "magnify %f\n", _magnification);
		    _magnification = 0;
		} else if(_rotation ==1) { 
               	    sendMsgii(ROTATE, 0, 0);
		} else { 

		NSPoint deltaOrigin = calcDeltaOrigin();

		/*
       		[_initialTouches[0] release];
       		[_currentTouches[0] release];
       		[_initialTouches[1] release];
       		[_currentTouches[1] release];
		if (_initialTouchCount > 2) {
       			[_initialTouches[2] release];
       			[_currentTouches[2] release];
		}
		*/

		// two finger, up, down, left, right 
		int param = LEFT; 
		float deltaX = fabs(deltaOrigin.x); 
		float deltaY = fabs(deltaOrigin.y); 
		if (deltaX > deltaY) {
			// left and right
			if (deltaOrigin.x > 0)
				param = RIGHT;
		} else {
			// up and down 
			if (deltaOrigin.y > 0)
				param = UP;
			else 
				param = DOWN;
		}
                if (!(deltaX == 0.0 && deltaY == 0.0)) {
			double diff = now -_prev_time;
			if (_maxTouchCount == 2) {
				if (diff < 1) {
					sendMsgii(CONNECT_TWO, param, 0);
				} else {
					sendMsgii(TWO_FINGERS, param, 0);
				}
			} else {
				if (diff < 1) {
					sendMsgii(CONNECT_THREE, param, 0);
				} else {
					sendMsgii(THREE_FINGERS, param, 0);
				}
			}
		}
       		fprintf(stderr, "end gesture : %f %f %d\n", deltaOrigin.x, deltaOrigin.y, _maxTouchCount);
		}

		_begin_flag = false;
		_initialTouchCount = 0;
		_currentTouchCount = 0;
		_maxTouchCount = 0;
		_rotation = 0;
            	retVal = NULL;
		_prev_time = getTime();
	}
    }
    /*
    else if(event.type == NSEventTypeSwipe) {
        if (captured) {
        	fprintf(stderr, "swipe\n");
	}
    } */
    else if(event.type == NSEventTypeMagnify) {
        if (captured) {
		_magnification = event.magnification;
        	//fprintf(stderr, "magnify %f\n", event.magnification);
                //sendMsgff(MAGNIFY, event.magnification, 0);
	}
    }
    else if(event.type == NSEventTypeRotate) {
        if (captured) {
		_rotation = 1;
        	//fprintf(stderr, "rotate \n");
                //sendMsgii(ROTATE, 0, 0);
	}
    }
    else if (type == kCGEventMouseMoved || type == kCGEventLeftMouseDragged || 
            type == kCGEventRightMouseDragged || type == kCGEventOtherMouseDragged) {

        //fprintf(stderr, "---- capture check\n");
        // capture/release the pointer if its on the top of the screen for a while	
        bool onCaptureEdge = false;
        bool onReleaseEdge = false;
        CGPoint warpLocation;
        warpLocation.x = location.x;
        warpLocation.y = location.y;

        // adjust the normalized coord reported to SAGE
        float x = location.x/screenWidth;
        float y = 1 - location.y/screenHeight;

        // figure out edge-specific cursor coordinates
        int off = 70;

        bool cornerX = true; // allow corner
        bool cornerY = true;
        if (disableCorners) {
            int cs = 40; // corner size
            cornerX = (location.x >= cs && location.x <= screenWidth-cs);
            cornerY = (location.y >= cs && location.y <= screenHeight-cs);
        }

        if (captureEdge == TOP_EDGE) {
            if (location.y < 2 && !captured && cornerX) {
                warpLocation.y = screenHeight-off; // jump close to bottom
                onCaptureEdge = true;
            }
            else if (location.y > screenHeight-2 && captured) {
                warpLocation.y = 10.0; // jump to top
                onReleaseEdge = true;
            }
        }	
        else if (captureEdge == BOTTOM_EDGE) {
            if (location.y < 2 && captured ) {
                warpLocation.y = screenHeight-off; // jump close to bottom	
                onReleaseEdge = true;
            }
            else if (location.y > screenHeight-2 && !captured && cornerX ) {
                warpLocation.y = 10.0; // jump to top
                onCaptureEdge = true;
            }
        }	
        else if (captureEdge == LEFT_EDGE) {
            if (location.x < 2 && !captured && cornerY ) {
                warpLocation.x = screenWidth-off; // jump close to bottom
                onCaptureEdge = true;
            }
            else if (location.x > screenWidth-2 && captured ) {
                warpLocation.x = 10.0; // jump to top
                onReleaseEdge = true;
            }
        }	
        else if (captureEdge == RIGHT_EDGE) {
            if (location.x < 2 && captured )  {
                warpLocation.x = screenWidth-off; // jump close to bottom
                onReleaseEdge = true;
            }
            else if (location.x > screenWidth-2 && !captured && cornerY ) {
                warpLocation.x = 10.0; // jump to top
                onCaptureEdge = true;
            }
        }	


        // determine what to do if the pointer is near the edge
        if (onCaptureEdge && !captured ) {  // CAPTURE
            if (firstEdgeTime < 0)   // time was reset
                firstEdgeTime = now;

            else if (now - firstEdgeTime > CURSOR_HOVER_TIME) {
                firstEdgeTime = -1.0;
                CGWarpMouseCursorPosition(warpLocation);  // warp the cursor to the bottom of the screen

                // adjust the normalized coord reported to SAGE
                x = warpLocation.x/screenWidth;
                y = 1 - warpLocation.y/screenHeight;

                toggleCapture();
            }
        }
        else if (onReleaseEdge && captured) {  // RELEASE
            if (firstEdgeTime < 0)   // time was reset
                firstEdgeTime = now;

            else if (now - firstEdgeTime > CURSOR_HOVER_TIME) {
                firstEdgeTime = -1.0;
                CGWarpMouseCursorPosition(warpLocation);  // warp the cursor to the bottom of the screen

                // adjust the normalized coord reported to SAGE
                x = warpLocation.x/screenWidth;
                y = 1 - warpLocation.y/screenHeight;

                toggleCapture();  
            }
        }
        else  // reset the timer
            firstEdgeTime = -1.0;

        //fprintf(stderr, "\nPos: %.3f %.3f", x, y);

        if (captured) {
            // dont send too frequently...
            if (now - lastMsgTime >= (1.0/50.0)) {
                lastMsgTime = now;
                sendMsgff(MOVE, x, y);
            }
            retVal = NULL;
        }
    }
    else if(type == kCGEventLeftMouseDown) {
        if (captured) {
            if(!doubleClick) {
                if (ctrl)
                    sendMsgii(CLICK, RIGHT, 1);
                else
                    sendMsgii(CLICK, LEFT, 1);
            }
            retVal = NULL;
        }
    }
    else if(type == kCGEventLeftMouseUp) {
        if (captured) {
            if (doubleClick)
                sendMsgi(DOUBLE_CLICK, LEFT);
            else {
                if (ctrl)
                    sendMsgii(CLICK, RIGHT, 0);
                else
                    sendMsgii(CLICK, LEFT, 0);
            }
            retVal = NULL;
        }
    }
    else if(type == kCGEventRightMouseDown) {
        if (captured) {
            sendMsgii(CLICK, RIGHT, 1);
            retVal = NULL;
        }
    }
    else if(type == kCGEventRightMouseUp) {
        if (captured) {
            sendMsgii(CLICK, RIGHT, 0);
            retVal = NULL;
        }
    }
    else if(type == kCGEventOtherMouseDown) {
        if (captured) {
            sendMsgii(CLICK, MIDDLE, 1);
            retVal = NULL;
        }
    }
    else if(type == kCGEventOtherMouseUp) {
        if (captured) {
            sendMsgii(CLICK, MIDDLE, 0);
            retVal = NULL;
        }
    }
    else if(type == kCGEventScrollWheel) {
        if (captured) {
            int numSteps = CGEventGetIntegerValueField(eventRef, kCGScrollWheelEventPointDeltaAxis1);
            int n;
            if (numSteps > 0) 
                n = -1 * (int)ceil((numSteps)/2.0); 
            else 
                n = -1 * (int)floor((numSteps)/2.0); 
            sendMsgi(WHEEL, n);
            retVal = NULL;
        }
    }
    else if(type == kCGEventKeyDown) {
        int keycode = CGEventGetIntegerValueField(eventRef, kCGKeyboardEventKeycode);
        //fprintf(stderr, "\nEvent: %d", keycode);
        /**
        if ( (( ((CGEventGetFlags(eventRef) & kCGEventFlagMaskAlphaShift)==kCGEventFlagMaskAlphaShift) || captured) && keycode==48) || (keycode == 113 || keycode == 105 || keycode == 107) ) {
            if (captured) { 
                captured = false;
                //fprintf(stderr, "\n\nRELEASED!!!!\n");
                printf("%d\n", RELEASED);
                CGDisplayShowCursor(kCGNullDirectDisplay);
                fflush(stdout);
            } 
            else {
                //fprintf(stderr, "\n\n===> CAPTURED\n");
                printf("%d\n", CAPTURED);
                printf("%d\n", HAS_DOUBLE_CLICK);
                fflush(stdout);
                captured = true;
                retVal = NULL;
            }
        }
        **/
    }

    else if(type == kCGEventFlagsChanged) {

        new = CGEventGetFlags(eventRef); 
        flagDiff = (int)old - new;
        old = new;

        // check for CTRL key (for right click on laptops)
        if (abs(flagDiff) == kCGEventFlagMaskControl+1) {
            if (captured) {
                if (abs(flagDiff) == kCGEventFlagMaskControl+1) {
                    if (flagDiff > 0)
                        ctrl = false;
                    else
                        ctrl = true;
                }
                retVal = NULL;
            }
        }

        // check for SHIFT key
        //
        /** disable activating pointer sharing with keyboard
         *
        else if (abs(flagDiff+2) == kCGEventFlagMaskShift || abs(flagDiff+4) == kCGEventFlagMaskShift) {
            if (flagDiff < 0) {
                if ( now - firstClickTime >= DOUBLE_CLICK_SPEED ) {
                    shiftCount = 1;
                    firstClickTime = now;
                }
                else if(shiftCount == 0) {
                    shiftCount = 1;
                }
                else if(shiftCount == 1) {
                    toggleCapture();
                    firstClickTime = 0.0;  // reset the timer
                }
            }
            retVal = NULL;
        }
        */
    }

    else if(type == kCGEventTapDisabledByTimeout) {
        //fprintf(stderr, "\n\nEVENT TAP DISABLED!!!!!!!!!\n\n");
        ctrl = false;
        old = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
        CGEventTapEnable(eventTap, true);
        return eventRef;	// NULL also seems to work here...
    }


    return retVal;
}


void quit()
{
    captured = false;
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
    CFRunLoopStop(CFRunLoopGetCurrent());
}


void timerCall(CFRunLoopTimerRef timer, void *info)
{
    char * res;
    char incomingMsg[10];  // from python side thru stdin
    int msgCode, msgVal;

    // check for the quit signal on stdin from the python side
    res = fgets(incomingMsg, 1000, stdin);

    if(feof(stdin))
        quit();
    else {  // actual message
        sscanf(incomingMsg, "%d %d\n", &msgCode, &msgVal);

        if (msgCode == QUIT)
            quit();
        else if(msgCode == CHANGE_EDGE)
            captureEdge = msgVal;
        else if(msgCode == DISABLE_CORNERS)
            disableCorners = (bool)msgVal;
    }

    if (capturedTime > 0.0 && getTime()-capturedTime > HIDE_CURSOR_DELAY) {
        capturedTime = -1.0;
        CGDisplayHideCursor(kCGNullDirectDisplay);
    }
}


int main()
{
    CGEventMask        eventMask;
    CFRunLoopTimerRef  timer;

    // get the current flag status
    old = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);

    // set stdin to non-blocking... instead of using select
    int fd = fileno(stdin);
    int flags = fcntl(fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);


    // The screen size of the primary display.
    CGRect screenBounds = CGDisplayBounds(CGMainDisplayID());
    screenWidth = (int)screenBounds.size.width;
    screenHeight = (int)screenBounds.size.height;
    fprintf(stderr, "width = %d, height = %d\n", screenWidth, screenHeight);

    // timing stuff
    gettimeofday(&tv_start,0);
    lastMsgTime = getTime();
    firstClickTime = getTime();
    firstEdgeTime = -1.0;
    capturedTime = -1.0;

    // enable showing/hiding of cursor... a hack really
    void CGSSetConnectionProperty(int, int, CFStringRef, CFBooleanRef);
    int _CGSDefaultConnection();
    CFStringRef propertyString;

    // Hack to make background cursor setting work
    propertyString = CFStringCreateWithCString(NULL, "SetsCursorInBackground", kCFStringEncodingUTF8);
    CGSSetConnectionProperty(_CGSDefaultConnection(), _CGSDefaultConnection(), propertyString, kCFBooleanTrue);
    CFRelease(propertyString);

    // Create an event tap. We are interested in mouse movements.
    /*
    eventMask = ((1 << kCGEventMouseMoved) | 
            (1 << kCGEventLeftMouseDown) | 
            (1 << kCGEventLeftMouseUp) |
            (1 << kCGEventLeftMouseDragged) |
            (1 << kCGEventRightMouseDown) | 
            (1 << kCGEventRightMouseUp) |
            (1 << kCGEventRightMouseDragged) |
            (1 << kCGEventOtherMouseDown) |
            (1 << kCGEventOtherMouseUp) |
            (1 << kCGEventOtherMouseDragged) |
            (1 << kCGEventScrollWheel) |
            (1 << kCGEventKeyDown) |
            (1 << kCGEventFlagsChanged));
    */
    // hyejung
    eventMask = NSMouseMovedMask | 
            NSLeftMouseDownMask | 
            NSLeftMouseUpMask |
            NSLeftMouseDraggedMask |
            NSRightMouseDownMask | 
            NSRightMouseUpMask |
            NSRightMouseDraggedMask |
            NSOtherMouseDownMask |
            NSOtherMouseUpMask |
            NSOtherMouseDraggedMask |
            NSEventMaskGesture |
            NSEventMaskMagnify |
            NSEventMaskRotate |
            NSEventMaskBeginGesture |
            NSEventMaskEndGesture |
            NSKeyDownMask |
            NSFlagsChangedMask;

            //NSEventMaskSwipe |

    eventTap = CGEventTapCreate(
            kCGSessionEventTap, kCGHeadInsertEventTap,
            0, eventMask, myCGEventCallback, NULL);

    if (!eventTap) {
        fprintf(stderr, "failed to create event tap\n");
        exit(1);
    }

    // Create a run loop source.
    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

    // create a timer
    timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 
            0.01, //interval in seconds
            0,
            0,
            &timerCall,
            NULL);

    // Add to the current run loop.
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
    CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);

    // Enable the event tap.
    CGEventTapEnable(eventTap, true);

    // Set it all running.
    CFRunLoopRun();

    return 0;
}



